package com.zhangtao.demo.controller;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.zhangtao.demo.common.constant.RedisConstant;
import com.zhangtao.demo.common.dto.Holder;
import com.zhangtao.demo.common.enums.RedisData;
import com.zhangtao.demo.common.util.RedisUtils;
import com.zhangtao.demo.common.util.ThrowableUtil;
import com.zhangtao.demo.common.vo.KeyInfos;
import com.zhangtao.demo.common.vo.RedisHashData;
import com.zhangtao.demo.common.vo.RedisKeyDataBase;
import com.zhangtao.demo.common.vo.RedisZSetData;
import com.zhangtao.demo.config.redis.jedis.JedisApi;

import redis.clients.jedis.ScanResult;
import redis.clients.jedis.Tuple;
import redis.clients.util.Slowlog;

@RequestMapping("/jedis")
@RestController
public class JedisController {

	private static final Logger logger = LogManager.getLogger(JedisController.class);
	@Autowired
	private JedisApi redisApi;
	public final String keySpace = "info keyspace";
	private int defaultScanCount = 100;
	private static final List<String> EMPTY_LIST_STR = new ArrayList<>(0);

	@RequestMapping(value = "/changeRedisHost", method = RequestMethod.POST)
	public boolean changeRedisHost(@RequestParam(value = "host", defaultValue = "localhost") String host,
			@RequestParam(value = "port", defaultValue = "6379") int port,
			@RequestParam(value = "db", defaultValue = "0") int db,
			@RequestParam(value = "pass", required = false) String pass) {
		boolean isChange = redisApi.changeHost(host, port, pass, db);
		return isChange;
	}

	@RequestMapping(value = "/selectDb/{db}", method = RequestMethod.GET)
	public ScanResult<String> changeDb(@PathVariable(value = "db") int db) {
		if (db < 0 || db > 15) {
			logger.warn("unexpect db : {} !", db);
			return null;
		}
		// 选择数据库
		redisApi.select(db);
		// 查询50条数据并返回
		ScanResult<String> response = redisApi.scan(0, defaultScanCount, "*");
		return response;
	}

	@RequestMapping(value = "/fetchSlowlog", method = RequestMethod.GET)
	public List<Slowlog> getSlowlog(@RequestParam(value = "count", defaultValue = "10") Long fetchCount) {
		List<Slowlog> list = redisApi.slowlog(fetchCount);
		return list;
	}

	@RequestMapping(value = "/info", method = RequestMethod.GET)
	public Map<String, JSONObject> info(String section) {
		String serverInfo = redisApi.info(section);
		Map<String, JSONObject> serverInfoM = new HashMap<>();
		String[] infos = StringUtils.split(serverInfo, RedisUtils.COMMOND_END_STR);
		int infosAmount = infos.length;
		Holder<JSONObject> lastItemInfo = new Holder<JSONObject>();
		Holder<String> lastItem = new Holder<String>();
		for (int i = 0; i < infosAmount; i++) {
			String infoitem = infos[i];
			if (StringUtils.startsWith(infoitem, RedisUtils.SERVER_INFO_START)) {
				if (lastItem.value() != null) {
					serverInfoM.put(lastItem.value(), lastItemInfo.value());
				}
				lastItemInfo.value(new JSONObject());
				lastItem.value(infoitem.substring(1).trim());
			}
			int spliteIndex = infoitem.indexOf(":");
			if (spliteIndex > 0) {
				lastItemInfo.value().put(infoitem.substring(0, spliteIndex).trim(),
						infoitem.substring(spliteIndex + 1).trim());
			}
			if (i == (infosAmount - 1)) {
				serverInfoM.put(lastItem.value(), lastItemInfo.value());
			}
		}
		return serverInfoM;
	}

	@RequestMapping(value = "/hostInfo", method = RequestMethod.GET)
	public Map<String, JSONObject> getHostKeyspaceInfo() {
		String response = redisApi.info("keyspace");
		String[] datas = StringUtils.split(response, RedisUtils.COMMOND_END_STR);
		Map<String, JSONObject> db = new HashMap<String, JSONObject>();
		Stream.of(datas).filter(d -> !d.startsWith("#")).forEach(row -> {
			String[] dbInfo = StringUtils.split(row, ":");
			JSONObject info = new JSONObject();
			String[] infos = dbInfo[1].split(",");
			for (int i = 0, a = infos.length; i < a; i++) {
				String[] vs = infos[i].split("=");
				info.put(vs[0], vs[1]);
			}
			db.put(dbInfo[0].substring(2), info);
		});
		return db;
	}

	@RequestMapping(value = "/get/{key}")
	public String get(@PathVariable String key) {
		String result = redisApi.get(key);
		return result;
	}

	@RequestMapping(value = "/set")
	public boolean set(@RequestParam String key, @RequestParam String value) {
		return redisApi.set(key, value);
	}

	@RequestMapping(value = "/del", method = RequestMethod.POST)
	public long del(@RequestParam String key) {
		long result = redisApi.del(key);
		return result;
	}
	@PostMapping(value = "/delByPattern")
	public long delByPattern(@RequestParam String delPattern) {
		ScanResult<String> scanResult = redisApi.scan(0, 1000, delPattern);
		List<String> ss = null;
		String[] delKey = null;
		long delCount = 0;
		while (scanResult.getResult().size() > 0) {
			ss = scanResult.getResult();
			delKey = new String[ss.size()];
			for (int i = 0, max = ss.size(); i < max; i++) {
				delKey[i] = ss.get(i);
			}
			delCount += redisApi.del(delKey);
			if (scanResult.getCursor() > 0) {
				scanResult = redisApi.scan(scanResult.getCursor(), 1000, delPattern);
			} else {
				break;
			}
		}
		return delCount;
	}

	@RequestMapping(value = "/hset", method = RequestMethod.POST)
	public long hset(@RequestParam(value = "key") String key, @RequestParam(value = "field") String field,
			@RequestParam(value = "value") String value) {
		return redisApi.hset(key, field, value);
	}

	@RequestMapping(value = "/zset", method = RequestMethod.POST)
	public long zset(@RequestParam(value = "key") String key, @RequestParam(value = "score") double score,
			@RequestParam(value = "value") String value) {
		return redisApi.zset(key, score, value);
	}

	@RequestMapping(value = "/sadd", method = RequestMethod.POST)
	public long sadd(@RequestParam(value = "key") String key, @RequestParam(value = "value") String value) {
		return redisApi.sadd(key, value);
	}

	@RequestMapping(value = "/lset", method = RequestMethod.POST)
	public boolean lset(@RequestParam(value = "key") String key, @RequestParam(value = "index") int index,
			@RequestParam(value = "value") String value) {
		return redisApi.lset(key, index, value);
	}

	@RequestMapping(value = "/rpush", method = RequestMethod.POST)
	public long rpush(@RequestParam(value = "key") String key, @RequestParam(value = "value") String value) {
		return redisApi.rpush(key, value);
	}

	/**
	 * 
	 * @Title: getType
	 * @Description: 获取指定key的类型
	 * @param: @param key
	 * @param: @return
	 * @return: String
	 * @throws @author 张涛
	 * @Date 2018年11月20日 下午1:48:44
	 */
	@RequestMapping(value = "/type", method = RequestMethod.POST)
	public String getType(@RequestParam(value = "key") String key) {
		key = key.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
		String decodeKey = null;
		try {
			decodeKey = URLDecoder.decode(key, "utf-8");
		} catch (UnsupportedEncodingException e) {
			logger.error("decode error : {}", ThrowableUtil.getErrorInfoFromThrowable(e));
		}
		if (!redisApi.hasKey(decodeKey)) {
			return null;
		}
		String keyType = redisApi.keyType(decodeKey);
		return keyType;
	}

	@RequestMapping(value = "/getByType")
	public Object getKeyByType(@RequestParam String key, @RequestParam String type,
			@RequestParam(required = false, defaultValue = "0") Integer nextIndex,
			@RequestParam(required = false, defaultValue = "*") String fieldPatter) {
		if (!RedisData.contains(type)) {
			return null;
		}
		Object result = null;
		if (!redisApi.hasKey(key)) {
			// key不存在
			return result;
		}
		switch (type) {
		case RedisConstant.REDIS_HASH:
			ScanResult<Map.Entry<String, String>> hresponse = redisApi.hscan(nextIndex, defaultScanCount, key,
					fieldPatter);
			result = hresponse;
			break;
		case RedisConstant.REDIS_STRING:
			result = redisApi.get(key);
			break;
		case RedisConstant.REDIS_LIST:
			int end = nextIndex + (defaultScanCount - 1);
			result = redisApi.lrange(key, nextIndex, end);
			break;
		case RedisConstant.REDIS_SET:
			ScanResult<String> sresponse = redisApi.sscan(nextIndex, defaultScanCount, key, fieldPatter);
			result = sresponse;
			break;
		case RedisConstant.REDIS_ZSET:
			end = nextIndex + (defaultScanCount - 1);
			Set<Tuple> zresponse = redisApi.zrange(key, nextIndex, end, true);
			result = zresponse;
			break;
		default:
			logger.warn("type [{}] not support!", type);
			break;
		}
		return result;
	}

	/**
	 * 
	 * @Title: addRedisKey
	 * @Description: 根据类型添加key
	 * @param: @param type
	 * @param: @param data
	 * @param: @return
	 * @return: boolean
	 * @throws @author 张涛
	 * @Date 2018年12月24日 下午1:55:24
	 */
	@RequestMapping(value = "/addRedisKey/{type}")
	public boolean addRedisKey(@PathVariable String type, @RequestBody JSONObject data) {
		if (data == null) {
			logger.error("data is empty!");
			return false;
		}
		if (!RedisData.contains(type)) {
			logger.warn("type[{}] not support!", type);
			return false;
		}
		boolean result = false;
		long count = 0;
		switch (type) {
		case RedisConstant.REDIS_HASH:
			RedisHashData hashdata = JSONObject.parseObject(data.toJSONString(), RedisHashData.class);
			count = redisApi.hset(hashdata.getKey(), hashdata.getField(), hashdata.getValue());
			result = count > 0;
			break;
		case RedisConstant.REDIS_STRING:
			RedisKeyDataBase stringdata = JSONObject.parseObject(data.toJSONString(), RedisKeyDataBase.class);
			result = redisApi.set(stringdata.getKey(), stringdata.getValue());
			break;
		case RedisConstant.REDIS_LIST:
			RedisKeyDataBase listgdata = JSONObject.parseObject(data.toJSONString(), RedisKeyDataBase.class);
			count = redisApi.rpush(listgdata.getKey(), listgdata.getValue());
			result = count > 0;
			break;
		case RedisConstant.REDIS_SET:
			RedisKeyDataBase setdata = JSONObject.parseObject(data.toJSONString(), RedisKeyDataBase.class);
			count = redisApi.sadd(setdata.getKey(), setdata.getValue());
			result = count > 0;
			break;
		case RedisConstant.REDIS_ZSET:
			RedisZSetData zsetdata = JSONObject.parseObject(data.toJSONString(), RedisZSetData.class);
			count = redisApi.zset(zsetdata.getKey(), zsetdata.getScore(), zsetdata.getValue());
			result = count > 0;
			break;
		default:
			logger.warn("type [{}] not support!", type);
			break;
		}
		return result;
	}

	@RequestMapping(value = "/scan/{nextIndex}")
	public ScanResult<String> scan(@PathVariable int nextIndex, @RequestParam(value = "keyPattern") String keyPattern) {
		// 查询50条数据并返回
		ScanResult<String> response = null;
		if (StringUtils.contains(keyPattern, "*") || StringUtils.isBlank(keyPattern)) {
			response = redisApi.scan(nextIndex, defaultScanCount, keyPattern);
		} else {
			Boolean has = redisApi.hasKey(keyPattern);
			if (has) {
				response = new ScanResult<>("0", Collections.singletonList(keyPattern));
			} else {
				response = new ScanResult<>("0", EMPTY_LIST_STR);
			}
		}
		return response;
	}

	@RequestMapping(value = "/scanTwo/{nextIndex}")
	public ScanResult<String> scanTwo(@PathVariable int nextIndex, @RequestParam(value = "keyPattern") String keyPattern) {
		// 查询50条数据并返回
		ScanResult<String> response = null;
		if (StringUtils.contains(keyPattern, "*") || StringUtils.isBlank(keyPattern)) {
			response = redisApi.scanN(nextIndex, defaultScanCount, keyPattern);
		} else {
			if (redisApi.hasKey(keyPattern)) {
				response = new ScanResult<>("0", Collections.singletonList(keyPattern));
			} else {
				response = new ScanResult<>("0", EMPTY_LIST_STR);
			}
		}
		return response;
	}
	@RequestMapping(value = "/getRedisKeysInfo")
	public KeyInfos getRedisKeysInfo(@RequestParam String key, @RequestParam String type) {
		KeyInfos info = new KeyInfos();
		info.setKey(key);
		info.setType(type);
		info.setTtl(redisApi.ttl(key));
		switch (type) {
		case RedisConstant.REDIS_STRING:
			info.setSize(redisApi.strlen(key));
			break;
		case RedisConstant.REDIS_LIST:
			info.setSize(redisApi.llen(key));
			break;
		case RedisConstant.REDIS_HASH:
			info.setSize(redisApi.hlen(key));
			break;
		case RedisConstant.REDIS_SET:
			info.setSize(redisApi.scard(key));
			break;
		case RedisConstant.REDIS_ZSET:
			info.setSize(redisApi.zcard(key));
			break;
		default:
			logger.warn("type [{}] not support!", type);
			break;
		}
		return info;
	}

	@RequestMapping(value = "/hdel", method = RequestMethod.POST)
	public long hdel(@RequestParam(value = "key") String key, @RequestParam(value = "field") String field) {
		return redisApi.hdel(key, field);
	}

	@RequestMapping(value = "/sdel", method = RequestMethod.POST)
	public long sdel(@RequestParam(value = "key") String key, @RequestParam(value = "member") String member) {
		return redisApi.srem(key, member);
	}

	@RequestMapping(value = "/zdel", method = RequestMethod.POST)
	public long zdel(@RequestParam(value = "key") String key, @RequestParam(value = "member") String member) {
		return redisApi.zrem(key, member);
	}

	@RequestMapping(value = "/ldel", method = RequestMethod.POST)
	public long ldel(@RequestParam(value = "key") String key, @RequestParam("index") int index,
			@RequestParam(value = "member", required = false) String member) {
		if (StringUtils.isBlank(member)) {
			return redisApi.lrem(key, index);
		}
		return redisApi.lrem(key, index, member);
	}
}
